#! /usr/bin/env python

# TODO: As an optimization measure the database can be sharded based on the username (using hash for example)

# TODO: Modify status of returned entities (it is usually calculated)
# The same for estimated finish time for pomodoros

# TODO: Replace XSLT with XPath, because XSLT returns empty elements instead of null
# so there are no 404 returned

from wsgiref.simple_server import make_server
from vsws import url_pattern, Controller
import model
import libxml2
import libxslt
import re
import time
from barrel import cooper

HOST = '172.16.11.107'
#HOST = 'laptop'
#HOST = 'localhost'
HOMEDIR = "/home/kostya/NetBeansProjects/PomodoroServer/src"
XSLDIR = HOMEDIR + "/xsl"
# TODO: Make it configurable per-user in order to customize appearance
xsl_base = '/ui'

# TODO: Implement document saving (flushing) on a regular basis
doc = libxml2.parseFile("%s/users-test.xml" % HOMEDIR)
ctxt = doc.xpathNewContext()
ctxt.xpathRegisterNs('pom', "http://www.pomodorotechnique.com/server")

xsl_users = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/users.xsl" % XSLDIR))
xsl_user = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/user.xsl" % XSLDIR))
xsl_plans = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/plans.xsl" % XSLDIR))
xsl_plan = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/plan.xsl" % XSLDIR))
xsl_workitems = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/workitems.xsl" % XSLDIR))
xsl_workitem = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/workitem.xsl" % XSLDIR))
xsl_pomodoros = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/pomodoros.xsl" % XSLDIR))
xsl_pomodoro = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/pomodoro.xsl" % XSLDIR))
xsl_interruptions = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/interruptions.xsl" % XSLDIR))
xsl_interruption = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/interruption.xsl" % XSLDIR))
xsl_messages = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/messages.xsl" % XSLDIR))
xsl_message = libxslt.parseStylesheetDoc (libxml2.parseFile("%s/message.xsl" % XSLDIR))

re_username = re.compile(r'^[a-zA-Z0-9_\-@.]+$')
re_year = re.compile(r'^\d{4}$')
re_month = re.compile(r'^\d{2}$')
re_day = re.compile(r'^\d{2}$')
re_number = re.compile(r'^\d+$')

def get_today ():
	return time.strftime('%Y-%m-%d')

def get_now ():
	return time.strftime('%Y-%m-%dT%H:%M:%S')

# Those are 'invalid' in order to simplify return of 400 in case of syntax error
def invalid_username (user):
	return not re_username.match (user)

def invalid_year (year):
	return not re_year.match (year)

def invalid_month (month):
	return not (re_month.match (month) and int(month) >= 1 and int(month) <= 12)

def invalid_day (day):
	return not (re_day.match (day) and int(day) >= 1 and int(day) <= 31)

def invalid_positive_count (count):
	return invalid_index(count)

def invalid_title (title):
	return not title

def invalid_index (index):
	return not (re_number.match (index) and int(index) >= 1)

def invalid_duration (duration):
	return not (re_number.match (duration) and int(duration) >= 1)

def is_today (year, month, day):
	t = time.localtime()
	return t[0] == int(year) and t[1] == int(month) and t[2] == int(day)

def is_today_plan (plan_node):
	return get_today() == plan_node.prop('date')

def invalid_plan (year, month, day):
	return not is_today (year, month, day)

def invalid_state (state):
	return state not in ['completed', 'failed']

# TODO: Download and use "decorator"

# Raw XPath queries
def raw_get_root():
	return ctxt.xpathEval("/pom:users")[0]

def raw_get_users():
	return ctxt.xpathEval("/pom:users/pom:user")

def raw_get_user(username):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']" % username)

def raw_get_plans(username):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan" % username)

def raw_get_plans_year(username, year):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/pom:plan[substring(@date,1,4) = %s]" % (username, year))

def raw_get_plans_month(username, year, month):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/pom:plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s]" % (username, year, month))

def raw_get_plan(username, year, month, day):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/pom:plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]" % (username, year, month, day))

def raw_get_plan_by_date(username, date):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/pom:plan[@date='%s']" % (username, date))

def raw_get_workitems(username, year, month, day, workitem):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/pom:plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem" % (username, year, month, day))

def raw_get_workitem(username, year, month, day, workitem):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/pom:plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]" % (username, year, month, day, workitem))

def raw_get_pomodoros(username, year, month, day, workitem):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]/pom:pomodoro" % (username, year, month, day, workitem))

def raw_get_pomodoro(username, year, month, day, workitem, pomodoro):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]/pom:pomodoro[%s]" % (username, year, month, day, workitem, pomodoro))

def raw_get_interruptions(username, year, month, day, workitem, pomodoro):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]/pom:pomodoro[%s]/pom:interruption" % (username, year, month, day, workitem, pomodoro))

def raw_get_interruption(username, year, month, day, workitem, pomodoro, interruption):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]/pom:pomodoro[%s]/pom:interruption[%s]" % (username, year, month, day, workitem, pomodoro, interruption))

def raw_get_messages(username, year, month, day, workitem, pomodoro):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]/pom:pomodoro[%s]/pom:message" % (username, year, month, day, workitem, pomodoro))

def raw_get_message(username, year, month, day, workitem, pomodoro, message):
	return ctxt.xpathEval("/pom:users/pom:user[@id='%s']/plan[substring(@date,1,4) = %s and substring(@date,6,2) = %s and substring(@date,9,2) = %s]/pom:workitem[%s]/pom:pomodoro[%s]/pom:message[%s]" % (username, year, month, day, workitem, pomodoro, message))

@url_pattern("/users", ['GET'])
def get_users ():
	result = xsl_users.applyStylesheet(doc, {})
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users", ['POST'])
def post_user (id, name, password, pomodorolen = '25', breaklen = '5'):
	if invalid_username (id) or invalid_title (name) or invalid_title (password) or invalid_duration(pomodorolen) or invalid_duration(breaklen):
		return {'status': 400}
	if raw_get_user(id):
		return {'status': 409}
	node = raw_get_root()
	newnode = doc.newDocNode(doc, 'user', '')
	newnode.newProp('id', id)
	newnode.newProp('name', name)
	newnode.newProp('password', password)
	newnode.newProp('pomodoro-length', pomodorolen)
	newnode.newProp('break-length', breaklen)
	newnode.newProp('regdate', get_now())
	node.addChild(newnode)
	return {'status': 201, 'content_type': 'text/xml', 'body': newnode}

# Decorator for request handlers
# It just stores all handler functions in a list inside UrlCallableFunction class
def validate_parameter (parameter, regexp):
	def wrap (f):
		def new_function (*args, **kw):
			print "Should check here... %s against %s" % (args, regexp)
			return f (*args, **kw)
		return decorator(new_function, f)
	return wrap

@url_pattern('/users/${username}', ['GET'])
#@validate_parameter('username', r'^[a-zA-Z0-9_-]+$')
#@authorisation_role('AUTHORISED_USER')
#@xpath_result("/users/user[@id = '${username}']")
#@wrap_results('plans')
def get_user (username):
	if invalid_username (username):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'"}
	result = xsl_user.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern('/users/${username}', ['PUT'])
def put_user (username, name = None, password = None, pomodorolen = None, breaklen = None):
	if invalid_username (username) or (name and invalid_title (name)) or (password and invalid_title (password)) or (pomodorolen and invalid_duration(pomodorolen)) or (breaklen and invalid_duration(breaklen)):
		return {'status': 400}
	nodes = raw_get_user(username)
	if raw_get_user(username):
		node = nodes[0]
		if name: 
			node.setProp('name', name)
		if password: 
			node.setProp('password', password)
		if pomodorolen: 
			node.setProp('pomodoro-length', pomodorolen)
		if breaklen: 
			node.setProp('break-length', breaklen)
		return {'status': 200, 'content_type': 'text/xml', 'body': node}
	else:
		return {'status': 404}

@url_pattern('/users/${username}', ['DELETE'])
def delete_user (username):
	if invalid_username (id):
		return {'status': 400}
	nodes = raw_get_user(id)
	if raw_get_user(id):
		node = nodes[0]
		res = str(node)
		# TODO: Complete - delete this node (use get_parent?)
		return {'status': 200, 'content_type': 'text/xml', 'body': res}
	else:
		return {'status': 404}

@url_pattern("/users/${username}/plans", ['GET'])
@url_pattern("/users/${username}/plans/${year}", ['GET'])
@url_pattern("/users/${username}/plans/${year}/${month}", ['GET'])
def get_plans (username, year = None, month = None):
	if invalid_username (username) or (year and invalid_year (year)) or (month and invalid_month (month)):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'"}
	if year:
		params['year'] = "'" + year + "'"
	if month:
		params['month'] = "'" + month + "'"
	result = xsl_plans.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans", ['POST'])
def post_plan (username):
	if invalid_username (username):
		return {'status': 400}
	# Check plan does not exist
	if raw_get_plan_by_date(username, get_today()):
		return {'status': 409}
	nodes = raw_get_user(username)
	if nodes:
		node = nodes[0]
		newnode = doc.newDocNode(doc.ns(), 'plan', '')
		newnode.newProp('status', 'new')
		newnode.newProp('date', get_today())
		node.addChild(newnode)
		return {'status': 201, 'content_type': 'text/xml', 'body': newnode}
	else:
		# User not found
		return {'status': 404}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}", ['GET'])
def get_plan (username, year, month, day):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'"}
	result = xsl_plan.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems", ['GET'])
def get_workitems (username, year, month, day):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'"}
	result = xsl_workitems.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}
		
@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems", ['POST'])
def post_workitem (username, year, month, day, title, pomodoros):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_title(title) or invalid_positive_count(pomodoros):
		return {'status': 400}
	if invalid_plan(year, month, day): return {'status': 409}
	nodes = raw_get_plan(username, year, month, day)
	if nodes:
		node = nodes[0]
		plan_state = get_plan_state(node)
		if plan_state in ['new', 'started']:
			newnode = doc.newDocNode(doc.ns(), 'workitem', '')
			if plan_state == 'started':
				newnode.newProp('section', 'unplanned')
			else:
				newnode.newProp('section', 'planned')
			newnode.newProp('status', 'new')
			newnode.newProp('title', title)

			# Add pomodoros
			for i in range(0, int(pomodoros)):
				pomodoro = doc.newDocNode(doc.ns(), 'pomodoro', '')
				pomodoro.newProp('planned', 'true')
				pomodoro.newProp('status', 'new')
				newnode.addChild(pomodoro)

			node.addChild(newnode)
			return {'status': 201, 'content_type': 'text/xml', 'body': newnode}
		else:
			return {'status': 409}
	else:
		# Plan not found
		return {'status': 404}
	
@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}", ['GET'])
def get_workitem (username, year, month, day, workitem):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem}
	result = xsl_workitem.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}", ['PUT'])
def put_workitem (username, year, month, day, workitem, state):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_state (state):
		return {'status': 400}
	
	nodes = raw_get_workitem(username, year, month, day, workitem)
	# TODO: Check rules
	if nodes:
		node = nodes[0]
		oldstate = get_workitem_state(node)
		if oldstate == 'new':
			if not state:
				state = 'started'
			if state == 'started':
				# TODO: Start first pomodoro
				# Check if no other pomodoros are started in the plan
				pass
			elif state in ['failed', 'completed']:
				pass
			else:
				return {'status': 409}
		elif oldstate == 'started':
			if not state:
				state = 'completed'
			if state == 'failed':
				# TODO: Abort a started pomodoro, if exists
				pass
			elif state == 'completed':
				# TODO: Check if a started pomodoro exist
				# Throw 409 if yes
				pass
			else:
				return {'status': 409}
		else:
			return {'status': 409}

		node.setProp("state", state)
		return {'status': 200, 'content_type': 'text/xml', 'body': node}
	else:
		return {'status': 404}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros", ['GET'])
def get_pomodoros (username, year, month, day, workitem):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem}
	result = xsl_pomodoros.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

def get_started_pomodoro (plan_node):
	for workitem in plan_node.get_children():
		if workitem.get_name() == 'workitem':
			for pomodoro in workitem.get_children():
				if pomodoro.get_name() == 'pomodoro':
					if pomodoro.prop('status') == 'started':
						return pomodoro

def get_plan_state (plan_node):
	if plan_node.prop('status') == 'completed':
		return 'completed'
	else:
		if is_today_plan(plan_node):
			for workitem in plan_node.get_children():
				if workitem.get_name() == 'workitem':
					for pomodoro in workitem.get_children():
						if pomodoro.get_name() == 'pomodoro':
							if pomodoro.prop('status') != 'new':
								return 'started'
			return 'new'
		else:
			return 'failed'

def get_workitem_state (workitem_node):
	if workitem_node.prop('status') == 'completed':
		return 'completed'
	else:
		if is_today_plan(workitem_node.get_parent()):
			for pomodoro in workitem_node.get_children():
				if pomodoro.get_name() == 'pomodoro':
					if pomodoro.prop('status') != 'new':
						return 'started'
			return 'new'
		else:
			return 'failed'

def get_pomodoro_state (pomodoro_node):
	status = pomodoro_node.prop('status')
	if status in ['completed', 'failed']:
		return status
	else:
		if is_today_plan(pomodoro_node.get_parent().get_parent()):
			return status
		else:
			return 'failed'

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros", ['POST'])
def post_pomodoro (username, year, month, day, workitem):
	print "post_pomodoro:"
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem):
		return {'status': 400}
	if invalid_plan(year, month, day): return {'status': 409}
	nodes = raw_get_workitem(username, year, month, day, workitem)
	if nodes:
		node = nodes[0]
		if get_workitem_state(node) in ['new', 'started']:
			newnode = doc.newDocNode(doc.ns(), 'pomodoro', '')
			if get_plan_state(node.get_parent()) == 'started':
				newnode.newProp('planned', 'false')
			else:
				newnode.newProp('planned', 'true')
			newnode.newProp('status', 'new')
			node.addChild(newnode)
			return {'status': 201, 'content_type': 'text/xml', 'body': newnode}
		else:
			return {'status': 409}
	else:
		# No work item found
		return {'status': 404}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}", ['GET'])
def get_pomodoro (username, year, month, day, workitem, pomodoro):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem, 'pomodoro' : pomodoro}
	result = xsl_pomodoro.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}", ['PUT'])
def put_pomodoro (username, year, month, day, workitem, pomodoro, state):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro) or invalid_state (state):
		return {'status': 400}
	# Increments pomodoro status. When pomodoro is registered, its status is set to new. 
	# When started, pomodoro's status is set to started. 
	# Any further status changes will seal the pomodoro. 
	# There can not exist more than one pomodoro in started state for the whole plan.

	# 1. Check that pomodoro exists
	# 2. Check it is new or started (here also plan state is checked)
	# 3. If new, then check there are no other pomodoros in started state
	# 4. Increment status and change timestamp
	
	nodes = raw_get_pomodoro(username, year, month, day, workitem, pomodoro)
	if nodes:
		node = nodes[0]
		oldstate = get_pomodoro_state(node)
		if oldstate == 'new':
			if not state:
				state = 'started'
			if state == 'started' and not get_started_pomodoro (node.get_parent().get_parent()):
				node.setProp('start', get_now())
				node.setProp('status', state)
				return {'status': 200, 'content_type': 'text/xml', 'body': node}
			else:
				return {'status': 409}
		elif oldstate == 'started':
			if not state:
				state = 'completed'
			if state in ['completed', 'failed']:
				node.setProp('finish', get_now())
				node.setProp('status', state)
				return {'status': 200, 'content_type': 'text/xml', 'body': node}
			else:
				return {'status': 409}
		else:
			return {'status': 409}
	else:
		return {'status': 404}
	

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}/interruptions", ['GET'])
def get_interruptions (username, year, month, day, workitem, pomodoro):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem, 'pomodoro' : pomodoro}
	result = xsl_interruptions.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}/interruptions", ['POST'], ['comment', 'duration'])
def post_interruption (username, year, month, day, workitem, pomodoro, comment, duration):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro) or (duration and invalid_duration (duration)):
		return {'status': 400}
	if invalid_plan(year, month, day): return {'status': 409}
	nodes = raw_get_pomodoro(username, year, month, day, workitem, pomodoro)
	if nodes:
		node = nodes[0]
		if get_pomodoro_state(node) == 'started':
			newnode = doc.newDocNode(doc.ns(), 'interruption', '')
			if comment:
				newnode.newProp('comment', comment)
			if duration:
				newnode.newProp('duration', duration)
			when = get_now()
			newnode.newProp('start', when)
			node.addChild(newnode)
			return {'status': 201, 'content_type': 'text/xml', 'body': newnode}
		else:
			return {'status': 409}
	else:
		# No pomodoro found
		return {'status': 404}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}/interruptions/${interruption}", ['GET'])
def get_interruption (username, year, month, day, workitem, pomodoro, interruption):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro) or invalid_index (interruption):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem, 'pomodoro' : pomodoro, 'interruption' : interruption}
	result = xsl_interruption.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}/messages", ['GET'])
def get_messages (username, year, month, day, workitem, pomodoro):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem, 'pomodoro' : pomodoro}
	result = xsl_messages.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}

@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}/messages", ['POST'])
def post_message (username, year, month, day, workitem, pomodoro, text):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro):
		return {'status': 400}
	if invalid_plan(year, month, day): return {'status': 409}
	nodes = raw_get_pomodoro(username, year, month, day, workitem, pomodoro)
	# TODO: Get origin from authorization token
	origin = 'kostya'
	if nodes:
		node = nodes[0]
		if get_pomodoro_state(node) == 'started':
			newnode = doc.newDocNode(doc.ns(), 'message', '')
			newnode.newProp('from', origin)
			when = get_now()
			newnode.newProp('when', when)
			newnode.newProp('text', text)
			node.addChild(newnode)
			return {'status': 201, 'content_type': 'text/xml', 'body': newnode}
		else:
			return {'status': 409}
	else:
		# No pomodoro found
		return {'status': 404}
	
@url_pattern("/users/${username}/plans/${year}/${month}/${day}/workitems/${workitem}/pomodoros/${pomodoro}/messages/${message}", ['GET'])
def get_message (username, year, month, day, workitem, pomodoro, message):
	if invalid_username (username) or invalid_year (year) or invalid_month (month) or invalid_day (day) or invalid_index (workitem) or invalid_index (pomodoro) or invalid_index (message):
		return {'status': 400}
	params = {'xsl-base' : "'" + xsl_base + "'", 'username' : "'" + username + "'", 'year' : "'" + year + "'", 'month' : "'" + month + "'", 'day' : "'" + day + "'", 'workitem' : workitem, 'pomodoro' : pomodoro, 'message' : message}
	result = xsl_message.applyStylesheet(doc, params)
	return {'content_type': 'text/xml', 'body': result}


# ***************** Built-in UI handling ********************

@url_pattern("/ui", ['GET'])
def get_ui_index ():
	result = open("%s/ui/%s" % (HOMEDIR, 'ui.html'), 'r').read()
	return {'content_type': 'text/html', 'body': result}

@url_pattern("/ui/${filename}.xsl", ['GET'])
def get_ui_file (filename):
	result = open("%s/ui/%s.xsl" % (HOMEDIR, filename), 'r').read()
	return {'content_type': 'text/xsl', 'body': result}

@url_pattern("/ui/${filename}.css", ['GET'])
def get_ui_file (filename):
	result = open("%s/ui/%s.css" % (HOMEDIR, filename), 'r').read()
	return {'content_type': 'text/css', 'body': result}

@url_pattern("/ui/${filename}.js", ['GET'])
def get_ui_file (filename):
	result = open("%s/ui/%s.js" % (HOMEDIR, filename), 'r').read()
	return {'content_type': 'text/javascript', 'body': result}

# ***************** Built-in Debugging ********************

# TODO: It won't work
@url_pattern("/testdata", ['GET'])
def get_testdata ():
    doc = libxml2.parseFile("%s/users-test.xml" % HOMEDIR)
    ctxt = doc.xpathNewContext()
    return {'status': '200'}

# ***************** Server initialization ********************

# Get all logins...
# TODO: Updated logins won't work until server restart
logins = []
for user in raw_get_users():
    logins += [(user.prop('id'), user.prop('password'))]

@cooper.basicauth(users=logins, realm='Pomodoro_Server_Login')
def authController(env, start_response):
	c = Controller()
	# env.get('com.saddi.service.session', {})
	return c(env, start_response)

while True:
	make_server(HOST, 8051, authController).handle_request()



#print Request.blank ('/users/kostya/plans/aa/2009?param1=aa&param2=bb&param1=cc').get_response (Controller())
#print Request.blank ('/users/username1/plans/2001/01/01/workitems/1/pomodoros/1/messages/1').get_response (Controller())
#print Request.blank ('/users/kostya/plans/2009/10/02/workitems/2/pomodoros/1.xml').get_response (Controller())

#print Request.blank ('/users').get_response (Controller())

#print Request.blank ('/users/kostya/plans/2009/10/10/workitems').get_response (Controller())

#result = xsl_plans.applyStylesheet(doc, {'username' : "'kostya'", 'year' : "'2009'", 'month' : "'10'"})
#result = style.applyStylesheet(doc, {'username' : "'kostya'", 'year' : "'2009'"})
#result = style.applyStylesheet(doc, {'username' : "'kostya'"})

#print result
#print result.children
